/*
 * Copyright (c) 2012 Intelygenz <www.intelygenz.com>
 * All rights reserved.
 * 
 * BSD License
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of copyright holders nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL COPYRIGHT HOLDERS OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * 
 * Description:
 * 
 * This JavaScript library provides different client-side optimization techniques for front construction.
 * It is independent and compatible with any server-side technology: JAVA, PHP, Python, GOOGLE APP ENGINE, .NET...
 * 
 * Dependences:
 * 	jQuery 1.5.1 +
 *	
 * Download:
 * 
 *  Previous version can be found at: https://iris-js.googlecode.com/svn/tags/
 *  e.g.:
 *  	https://iris-js.googlecode.com/svn/tags/0.3.1/iris.js
 * 
 * Creation date: 2012-01-12
 * 
 * [version] date -> authors
 * 		upd|fix|new|dep|rmv - description
 *
 *
 * [0.3.1] 2012-03-13 -> jonas.dacruz@intelygenz.com, angel.sanchez@intelygenz.com
 * 		[new] Screens and UI components inheritance
 * 		[new] AbstractComponent.InstanceUI()
 * 		[new] Automated dependency load
 * 		[new] Adoption Resource-View-Presenter pattern
 * 		[upd] Complete code refactor
 * 		[rmv] iris.screen.transition
 * 		
 * 		WARNING: No backward with previous versions
 * 
 * */

var iris = new function() {
	
	var _APP_VERSION = "0.3.1"
	,	_APP_NAME = "iris"
	,	_JQ_MIN_VER = 1.5
	;

	var _Env = null
	,	_Log = {"error":true}
	,	_LogPrefix = ""
	,	_Screen = {}
	,	_ScreenUrl = {}
	,	_Context = {}
	,	_LastScreen = {}
	,	_History = []
	,	_HistoryCurrent = 0
	,	_Global = {}
	,	_Local = {}
	,	_Locale = {}
	,	_Data = {}
	,	_Event = {}
	,	_Includes = {}
	,	_Components = {}
	,	_AppBaseUri = ""
	,	_LastIncludePath
	,	_Head = $("head").get(0)
	;

	var _NavigateSettings = {
		  "params"	 		: null
		, "saveInHistory" 	: true
		, "replaceInHistory": false
	};
	
	if( typeof jQuery === "undefined" ) {
		_E( "jQuery " + _JQ_MIN_VER + "+ previous load required" );
	}
	else if ( $().jquery < _JQ_MIN_VER ) {
		_E( "jQuery " + $().jquery + " currently loaded, jQuery " + _JQ_MIN_VER + "+ required" );
	}
	
	var _HasConsole = (window.console && window.console.debug && window.console.warn && window.console.error);
	if ( !_HasConsole && window.console && window.console.log ) {	// TODO: Think about remove it
		window.console.log("advanced console debugging is not supported in this browser");
	}
	
	function _AppName () {
		return _APP_NAME + " v" + _APP_VERSION + " [" + _Env + "]";
	}
	
	function _Include(p_uiFile) {
		
		if ( !_Includes.hasOwnProperty(p_uiFile) ) {
			_Includes[p_uiFile] = true; // TODO include status
			
			var fileUrl = _AppBaseUri + p_uiFile;
			_D("[iris.ui.Include]", fileUrl);
			
			if ( p_uiFile.lastIndexOf(".css") > -1 ) {
				var link  = document.createElement('link');
				link.rel = 'stylesheet';
				link.type = 'text/css';
				link.href = fileUrl;
				_Head.appendChild(link);
			}
			else {
				_LastIncludePath = p_uiFile;
				var isHtml = p_uiFile.lastIndexOf(".html") > -1;
				
				_AjaxSync(
					  fileUrl
					, isHtml ? "html" : "text"
					, function (p_data) {
						if ( isHtml ) {
							_IncludeHtml(p_data, p_uiFile);
						}
						else {
							_IncludeJs(p_data);
						}
					}
					, function (p_err) {
						delete _Includes[fileUrl];
						_E(p_err.status, "Error loading file", fileUrl);
					}
				);
			}
		}
	}
	
	function _IncludeHtml ( p_html, p_uiFile ) {
		_Includes[p_uiFile] = p_html;
	}
	
	function _IncludeJs ( p_js ) {
		var script = document.createElement("script");
		script.language = "javascript";
		script.type = "text/javascript";
		script.text = p_js;
		_Head.appendChild(script);
	}
	
	function _LogOf (p_type) {
		return _Log[p_type];
	}
	
	function _L(){
		if ( _HasConsole && window.console.log) {
			window.console.log(_LogPrefix, arguments);
		}
	};
	
	function _D(){
		if(_HasConsole && _LogOf("debug") ){
			window.console.debug(_LogPrefix, arguments);
		}
	};
	
	function _W(){
		if(_HasConsole && _LogOf("warning") ){
			window.console.warn(_LogPrefix, arguments);
		}
	};
	
	function _E(){
		if(_HasConsole && _LogOf("error") ){
			window.console.error(_LogPrefix, arguments);
		}
	};
	
	function _ConfigLoad (p_json){
		if ( p_json ) {
			_Data = p_json;

			_GlobalLoad( _Data["global"] );

			if ( _Data["log"] ) {
				var logConfig = _Data["log"][_GetEnv()];
				var logs = logConfig.split(",");
				for ( var logType in logs ) {
					_Log[ $.trim(logs[logType]) ] = true;
				}
			}
			
			_LocalLoad( _Data["local"] );
		}
		return _Data;
	};
	
	function _GetEnv (p_env) {
		if ( p_env ) {
			_Env = p_env;
		}
		else {
			if ( !_Env ) {
				_Env = _Data["environment-default"];
				for (var p in _Data["environment"] ){
					if ( document.location.href.indexOf( p ) > -1 ) {
						_Env = _Data["environment"][p];
						break;
					}
				}
				if ( !_Env ) {
					_Env = "pro";
				}
				_LogPrefix = "[" + _Env + "]";
			}
			return _Env;
		}
	};
	
	function _AjaxSync (p_uri, p_dataType, f_success, f_error) {
		$.ajax(
			{ url: p_uri
			, dataType: p_dataType
			, async: false
			, cache: true
			, success : f_success
			, error : f_error
			}
		);
	}
	
	function _GlobalLoad(p_hash){
		$.extend(_Global, p_hash);
		return _Global;
	};

	function _GlobalData (p_label, p_value){
		if ( p_label && p_value ) {
			_Global[p_label] = p_value; 	
		}
		else if ( p_label ) {
			return _Global[p_label];
		}
		else {
			return _Global;
		}
	};
	
	function _LocalLoad(p_hash){
		$.extend(_Local, p_hash);
		return _Local;
	};

	function _LocalData(p_label, p_value){
		if ( p_label && p_value ) {
			_Local[p_label][_GetEnv()] = p_value; 	
		}
		else if ( p_label ) {
			return _Local[p_label][_GetEnv()];
		}
		else  {
			return _Local;
		}
	};
	
	function _Find(p_eventName, f_func){
		var events = _Event[p_eventName];
		if ( events ) {
			for ( var f=0, F=events.length; f<F; f++ ) {
				if ( events[f] === f_func ) {
					return f;
				}
			}
		}
		return -1;
	}
	
	function _EventSubscribe(p_eventName, f_func){
		if ( !_Event[p_eventName] ) {
			_Event[p_eventName] = [];
		}

		var index = _Find( p_eventName, f_func );
		if ( index==-1 ) {
			index = _Event[p_eventName].length;
		}

		_Event[p_eventName][index] = f_func;
	};
	
	function _EventRemove(p_eventName, f_func){
		var index = _Find(p_eventName, f_func);
		if ( index!=-1 ){
			_Event[p_eventName].splice(index,1);
		}
	};

	function _EventNotify(p_eventName, p_data){
		if ( _Event[p_eventName] ) {
			var funcs = _Event[p_eventName];
			for ( var f=0, F=funcs.length; f<F; f++ ) {
				funcs[f](p_data);
			}
		}
	}
	
	function _BaseUri(p_baseUri){
		if ( p_baseUri ) {
			_AppBaseUri = p_baseUri;
		}
		else {
			var base = document.getElementsByTagName("base");
			base = base.length > 0 ? base[0].attributes["href"].value : "/";
			_AppBaseUri = document.location.protocol + "//" + document.location.host + base;
		}
		return _AppBaseUri;
	};

	function _LocaleLoad(p_locale, p_data){
		_Locale = p_locale;
		_Data[_Locale] = p_data;
	};

	function _LocaleGet(p_label, p_locale){
		var locale = ( p_locale ) ? p_locale : _Locale;
		var value  = _Data[locale][p_label];
		if ( !value ) value = "??" + p_label + "??";
		return value;
	};

	function _LocaleParse(p_html){
		var html = p_html;
		var matches = html.match(/@@[A-Z_\.]+@@/g);
		if ( matches ) {
			var f, F = matches.length;
			for ( f=0; f<F; f++ ) {
				html = html.replace(matches[f], _LocaleGet(matches[f].substring(2,matches[f].length-2)));
			}
		}
		return html;
	};
	
	function _LangLoad (p_locale, p_data) {
		_D("[iris.lang.Load]", p_locale, p_data);
		return _LocaleLoad(p_locale, p_data);
	};

	function _LangLoadFrom (p_locale, p_uri) {
		_D("[iris.lang.LoadFrom]", p_locale, p_uri);
		
		_AjaxSync(
			  p_uri
			, "json"
			, function (p_data){
				  _LocaleLoad(p_locale, p_data);
				  _D("[iris.lang.LoadFrom] loaded", p_data);
			  }
			, function (p_err) {				
				  _E(p_err.status, "Error loading lang file", p_uri);
			}
		);
	};

	function _HashToJq(p_hash, p_$obj, p_filter){
		var dom = p_$obj.get(0);
		if ( p_filter ){
			var filter;
			for ( var f=0, F=p_filter.length; f<F; f++ ){
				filter = p_hash[p_filter[f]];
				if ( filter ) {
					dom.setAttribute(p_filter[f], filter);
				}
			}
		}
		else {
			for ( var label in p_hash){
				dom.setAttribute(label, p_hash[label]);
			}
		}
		return p_$obj;
	}

	function _JqToHash(p_$obj) {
		var hash = {};
		var attrs = p_$obj.get(0).attributes;
		var label;
		for( var f=0, F=attrs.length; f<F; f++ ) {
			label = attrs[f].name;
			if ( label.indexOf("_")==0 ){
				label = label.substr(1);
			}
			hash[label] = attrs[f].value;
		}
		return hash;
	}

	function _TemplateCreate(p_htmlUrl, p_cssUrl){
		if ( p_cssUrl ) {
			_Include(p_cssUrl);
		}
		
		_Include(p_htmlUrl);
		
		var $tmpl = $( _LocaleParse(_Includes[p_htmlUrl]) );

		return {
			$Get : function (p_id) {
				if ( p_id ) {
					return $tmpl.find("[_id=" + p_id + "]");
				}
				return $tmpl;
			}
		};
	}
	
	function _InstanceUI (p_tmpl, p_uiId, p_jsUrl, p_uiSettings) {
		_Include(p_jsUrl);
		
		var uiInstance = new _Components[p_jsUrl]();
		uiInstance.__Id__ = p_uiId;
		
		var $uiContainer = p_tmpl.$Get(p_uiId);
		uiInstance.__$Container__ = $uiContainer;
		
		p_uiSettings = p_uiSettings || {};
		
		var jqToHash = _JqToHash($uiContainer);
		uiInstance.Create(jqToHash, p_uiSettings);
		
		return uiInstance;
	}
	
	function _Add (p_$context, p_screenId, p_jsUrl) {
		_Include(p_jsUrl);
		
		_ScreenUrl[p_screenId] = p_jsUrl;
		_Context[p_screenId] = p_$context;
		
		if ( p_$context.get(0) === document.body ) {
			p_$context.attr("_id", "document_body");
		}
	};
	
	function _CreateScreen (p_screenId) {
		var jsUrl = _ScreenUrl[p_screenId];
		var screenInstance = new _Components[jsUrl]();
		screenInstance.__Id__ = p_screenId;
		
		screenInstance.Create();
		screenInstance.Hide();
		
		_Context[p_screenId].append( screenInstance.$Get() );
		_Screen[p_screenId] = screenInstance;
	}

	function _Navigate (p_screenId, p_back, p_removeForwardHistory, p_settings){
		_D("[iris.screen.Goto]", p_screenId, p_settings);

		var settings = _MergeNavSettings(p_settings);

		if ( !_ScreenUrl.hasOwnProperty(p_screenId) ) {
			_E( "[iris.screen.Goto] iris.screen.Add() has to be previosly called", p_screenId );
		}
		else {
			if ( settings["replaceInHistory"] ) {
				settings["saveInHistory"] = true;
				_HistoryCurrent--;
			}

			if ( settings["saveInHistory"] ) {
				_History[_HistoryCurrent] = { "screenId": p_screenId, "params": settings["params"], "transition": settings["transition"] };
				_HistoryCurrent++;
				if ( p_removeForwardHistory ) {
					_History = _History.slice(0, _HistoryCurrent);
				}
			}

			if ( !_Screen.hasOwnProperty(p_screenId) ) {
				_CreateScreen(p_screenId);
			}

			var currentScreen = _Screen[p_screenId];
			var contextId = currentScreen.$Get().parent().attr("_id");

			if ( _LastScreen.hasOwnProperty(contextId) ) {
				var lastScreen = _LastScreen[contextId];
				lastScreen.__Sleep__();
				lastScreen.Hide();
			}
			currentScreen.__Awake__( settings["params"] );
			currentScreen.Show();

			_LastScreen[contextId] = currentScreen;

			_EventNotify( iris.event.SCREEN_NAVIGATION, { "current" : _HistoryCurrent, "total" : _History.length, "screenId" : p_screenId } );
		}
	}
	
	function _MergeNavSettings (p_settings) {
		var settings = $.extend({}, _NavigateSettings);
		$.extend(settings, p_settings);
		return settings;
	}
	
	function _Forward () {
		if ( _HistoryCurrent < _History.length ) {
			_D("[iris.screen.Forward]", _HistoryCurrent, _History);

			var nextScreen = _History[_HistoryCurrent];
	
			var settings = _MergeNavSettings({
				  "params" : nextScreen["params"]
				, "transition" : nextScreen["transition"]  
				, "saveInHistory" : false
				, "replaceInHistory" : false
			});

			_HistoryCurrent++;
			_D("[iris.screen.Forward]", nextScreen["screenId"], true, false, settings, _HistoryCurrent, _History);
			_Navigate(nextScreen["screenId"], false, false, settings);			
		}
		else {
			_E("[iris.screen.Forward]", "no more elements in history");	
		}		
	};

	function _Back() {
		_D("[iris.screen.Back]", _HistoryCurrent, _History);
		if ( _HistoryCurrent > 0 ) {
			_HistoryCurrent--;
			
			var prevScreen = _History[_HistoryCurrent-1];

			var settings = _MergeNavSettings({
				  "params" : prevScreen["params"]
				, "transition" : _History[_HistoryCurrent]["transition"]  
				, "saveInHistory" : false
				, "replaceInHistory" : false
			});
			_D("[iris.screen.Back]", prevScreen["screenId"], true, false, settings, _HistoryCurrent, _History);
			_Navigate(prevScreen["screenId"], true, false, settings);
		}
		else {
			_E("[iris.screen.Back]", "no more elements in history");	
		}
	};
	
	function _Goto (p_screenId, p_settings) {
		_Navigate(p_screenId, false, true, p_settings);
	};
	
	function _AbstractComponent () {
		this.__Template__ = null;
		this.__Id__ = null;
		this.__UIComponents__ = [];
		
		this.__Sleep__ = function () {
			for ( var f=0, F=this.__UIComponents__.length; f < F; f++ ) {
				this.__UIComponents__[f].Sleep();
			}
			this.Sleep();
		};
		
		this.__Awake__ = function (p_params) {
			for ( var f=0, F=this.__UIComponents__.length; f < F; f++ ) {
				this.__UIComponents__[f].Awake();
			}
			this.Awake(p_params);
		};
		
		
		this.Show = function () {
			this.$Get().show();
		};
		
		this.Hide = function () {
			this.$Get().hide();
		};
		
		this.$Get = function (p_id) {
			return this.__Template__.$Get(p_id);
		};
		
		this.Template = function (p_htmlUrl, p_cssUrl) {
			this.__Template__ = _TemplateCreate(p_htmlUrl, p_cssUrl);
		};
		
		this.InstanceUI = function (p_uiId, p_jsUrl, p_uiSettings) {
			uiInstance = _InstanceUI(this.__Template__, p_uiId, p_jsUrl, p_uiSettings);
			this.__UIComponents__.push(uiInstance);
			return uiInstance;
		};
		
		// To override functions
		this.Create = function () {};
		this.Awake = function () {};
		this.Sleep = function () {};
		this.Destroy = function () {};
	}
	
	(_AbstractUI = function () {
		this.__$Container__ = null;
		
		this.Append = function () {
			this.__$Container__.append( this.$Get() );
		};

		this.Replace = function () {
			this.__$Container__.replaceWith( this.$Get() );
		};
		
	}).prototype = new _AbstractComponent();
	
	(_AbstractScreen = function () {
		
	}).prototype = new _AbstractComponent();
	
	function _ScreenCreate (f_new) {
		f_new.prototype = new _AbstractScreen();
		_Components[_LastIncludePath] = f_new;
	}
	
	function _UICreate (f_new) {
		f_new.prototype = new _AbstractUI();
		_Components[_LastIncludePath] = f_new;
	};
	
	// public interfaces
	//
	this.screen = {};
	this.event = {};
	this.config = {};
	this.ui = {};
	this.net = {};
	this.global = {};
	this.local = {};
	this.lang = {};
	
	this.config.Load = _ConfigLoad;
	this.config.Env = _GetEnv;
	
	this.global.Load = _GlobalLoad;
	this.global.Data = _GlobalData;
	
	this.local.Load = _LocalLoad;
	this.local.Data = _LocalData;
	
	this.lang.Load = _LangLoad;
	this.lang.LoadFrom = _LangLoadFrom;
	this.lang.Get = _LocaleGet;
	
	this.L = _L;
	this.D = _D;
	this.W = _W;
	this.E = _E;
	
	this.event.Subscribe = _EventSubscribe;
	this.event.Notify = _EventNotify;
	this.event.Remove = _EventRemove;
	
	this.net.BaseUri = _BaseUri;
	
	this.Screen = _ScreenCreate;
	this.UI =  _UICreate;
	
	this.screen.Add = _Add;
	this.screen.Goto = _Goto;
	this.screen.Back = _Back;
	this.screen.Forward = _Forward;
	
	this.ui.JqToHash = _JqToHash;
	this.ui.HashToJq = _HashToJq;
	
	// Events
	this.event.SCREEN_NAVIGATION = "iris_event_OnScreenNavigation";
	
};
